﻿using CorpStore.Common;
using CorpStore.Models;
using CorpStore.ViewModels;
using Newtonsoft.Json;
using StoreHelper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.Storage.Search;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace CorpStore
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : BasePage
    {
        const int SECS_PER_FLIPVIEW = 5;
        MainViewModel viewModel = new MainViewModel();
        DispatcherTimer flipViewTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) }; // timer for automatic switch within flipview (start at 100 msecs to begin with to force an environment check)
        CoreDispatcher dispatcher;
        bool envChecked = false;

        public MainPage()
        {
            InitializeComponent();
            DataContext = viewModel;

            // capture our core dispatcher so we can run code on our UI thread
            dispatcher = Window.Current.Dispatcher;
        }


        public async void FileQuery_ContentsChangedAsync(IStorageQueryResultBase sender, object args)
        {
            await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                viewModel.StopListeningToFilesChanged(FileQuery_ContentsChangedAsync);
                handleLocalFilesChanged();
            });

        }

        private async void handleLocalFilesChanged()
        {
            // check if our master catalog file has been created
            string masterFn = "masterCatalog.json";
            await GenLib.SleepAsync(3000); // give some time for the master catalog to appear
            if (await StoreHelper.GenIO.FileExistsAsync(ApplicationData.Current.LocalFolder, masterFn))
            {
                // we have our master catalog file now - show message for it
                await viewModel.InitAsync(null);
                viewModel.ProgressIsActive = false;
                logInfoWithToast("Our master catalog file, '{0}', has been created successfully.  You may now start using CorpStore.", masterFn);
            }
            else
                logErrWithMsg("Our localState folder has changed but {0} does not exist", masterFn);
        }


        protected async override void LoadState(object navigationParameter, Dictionary<string, object> pageState)
        {
            // turn our auto flipview changer on
            flipViewTimer.Tick += flipViewTimer_Tick;
            flipViewTimer.Start();

            logInfo("At top of MainPage.LoadState");
            if (viewModel.GroupList == null)
            {
                // we have not yet loaded this page - do some init
                // first register our background task
                StoreHelper.GenLib.RegisterBackgroundTask("CorpStoreTasks.CheckForNewApps", /* fully qualified name of task class */
                                                            "CheckForNewApps", /* matches constant defined in our util class for name */
                                                            new MaintenanceTrigger(15, false), /* task runs every 15 min continually */
                                                            new SystemCondition(SystemConditionType.UserPresent)); /* require user present */

                // check app pre-requisities
                logInfo("About to call initAsync of viewModel");
                await viewModel.InitAsync(navigationParameter as NavData);

                // wire up our folder watcher to see if *.json changes to our localState folder 
                //await StoreHelper.GenLib.WatchFolderAsync(ApplicationData.Current.LocalFolder, new List<string>() { ".json" }, FolderDepth.Shallow, onLocalFolderChangedAsync);
            }
            logInfo("At bottom of MainPage.LoadState");
        }


        protected override void SaveState(Dictionary<string, object> pageState)
        {
            logInfo("At top of MainPage.SaveState");
            flipViewTimer.Tick -= flipViewTimer_Tick;
            flipViewTimer.Stop();
        }


        void flipViewTimer_Tick(object sender, object e)
        {
            if (flipView.SelectedIndex < flipView.Items.Count - 1)
                viewModel.SelectedFlipIndex++;
            else
                viewModel.SelectedFlipIndex = 0;

            // check environment if needed
            if (!envChecked)
            {
                envChecked = true;
                flipViewTimer.Interval = new TimeSpan(0,0,SECS_PER_FLIPVIEW); // change timer to every 5 secs now 
                viewModel.checkEnvironmentAsync(FileQuery_ContentsChangedAsync, false);
            }
        }

        /// <summary>
        /// Invoked when an item within a group is clicked.
        /// </summary>
        /// <param name="sender">The GridView (or ListView when the application is snapped)
        /// displaying the item clicked.</param>
        /// <param name="e">Event data that describes the item clicked.</param>
        private void ItemView_ItemClick(object sender, ItemClickEventArgs e)
        {
            showDetailsPage(e.ClickedItem as PackageInfo);
        }



        private void imgScreenshot_Tapped(object sender, TappedRoutedEventArgs e)
        {
            showDetailsPage((PackageInfo)flipView.SelectedItem);
        }


        private void flipView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // whenever selection changes (manually or automatically), restart our timer to give full duration to each view
            flipViewTimer.Stop();
            flipViewTimer.Start();
        }


        private void showDetailsPage(PackageInfo app)
        {
            NavData navData = new NavData() { CurrentApp = app };
            string serializedData = JsonConvert.SerializeObject(navData);
            Frame.Navigate(typeof(AppDetailsPage), serializedData);
        }


        private void showGroupDetailsPage(PackageInfoGroup group)
        {
            NavData navData = new NavData() { CurrentGroup = group };
            string serializedData = JsonConvert.SerializeObject(navData);
            Frame.Navigate(typeof(GroupDetailsPage), serializedData);
        }


        private void SearchBoxEventsSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
        {
            var deferral = args.Request.GetDeferral();
            try
            {
                List<PackageInfo> foundApps = viewModel.GetAppsByName(args.QueryText);
                foreach (PackageInfo app in foundApps)
                {
                    Uri imageUri = new Uri(app.LogoFn);
                    args.Request.SearchSuggestionCollection.AppendResultSuggestion(
                        app.DisplayName,
                        app.Version ?? "",
                        app.FamilyName,
                        RandomAccessStreamReference.CreateFromUri(imageUri),
                        "No Image Available");
                }
            }
            finally
            {
                deferral.Complete();
            }
        }


        private void SearchBoxSuggestions_ResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args)
        {
            PackageInfo app = viewModel.GroupList.Groups.SelectMany(g => g.Applications).Where(a => a.FamilyName.Equals(args.Tag)).FirstOrDefault();
            showDetailsPage(app);
        }

        private void logErr(string msg, params object[] p)
        {
            App.LogErr(msg, p);
        }

        private void logErrWithMsg(string msg, params object[] p)
        {
            App.LogErrWithMsg(msg, p);
        }

        private void logInfo(string msg, params object[] p)
        {
            App.LogInfo(msg, p);
        }

        private void logInfoWithMsg(string msg, params object[] p)
        {
            App.LogInfoWithMsg(msg, p);
        }

        private void logInfoWithToast(string msg, params object[] p)
        {
            App.LogInfoWithToast(msg, p);
        }

        private async void btnRefresh_Click(object sender, RoutedEventArgs e)
        {
            await viewModel.checkEnvironmentAsync(FileQuery_ContentsChangedAsync);
        }


        private async void btnRebuildCatalog_Click(object sender, RoutedEventArgs e)
        {
            await viewModel.LaunchUpdateMasterRequestAsync(FileQuery_ContentsChangedAsync);
        }


        private async void BasePage_Loaded(object sender, RoutedEventArgs e)
        {
            await StoreHelper.GenLib.SleepAsync(3000);
        }


        private async void onLocalFolderChangedAsync(Windows.Storage.Search.IStorageQueryResultBase sender, object args)
        {
            // refresh our list of apps since our localFolder contents has changed
            await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                viewModel.InitAsync(null);
            });
        } 



        private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
        {
            // whenever scrolled, restart our timer to give full duration to each view
            flipViewTimer.Stop();
            flipViewTimer.Start();
        }


        private void Header_Click(object sender, RoutedEventArgs e)
        {
            // Determine what group the Button instance represents
            var group = (sender as FrameworkElement).DataContext;

            // Navigate to the appropriate destination page, configuring the new page
            // by passing required information as a navigation parameter
            showGroupDetailsPage((PackageInfoGroup)group);
        }

    }
}
